extends Object


class_name ShapeDraw


## 添加直线
static func line_to(node: Node, start: Vector2, direction: Vector2) -> Line2D:
    return line_to_point(node, start, start + direction)

## 添加直线
static func line_to_point(node: Node, from: Vector2, to: Vector2, width: int = 1) -> Line2D:
    var line: Line2D = Line2D.new()
    line.add_point(from)
    line.add_point(to)
    line.width = width
    node.add_child(line)
    return line

## 返回矩形中心点以Vector2.RIGHT为初始方向,顺时针旋转指定角度后会产生交点的边框
static func get_rect_center_intersect_line(center_size: Vector2, angle: float) -> Dictionary:
    var w: float = center_size.x
    var h: float = center_size.y

    var compare_angle: float = atan(h / w)
    var v: float = abs(angle)
    # 左边
    if (v >= 0 and v <= compare_angle):
        return {"p1": Vector2(w, -h), "p2": Vector2(w, h), "normal": Vector2.LEFT, "desc": "right"}
    elif (v <= PI and v >= (PI - compare_angle)):
        return {"p1": Vector2(-w, -h), "p2": Vector2(-w, h), "normal": Vector2.RIGHT, "desc": "left"}
    elif angle < 0:
        return {"p1": Vector2(-w, -h), "p2": Vector2(w, -h), "normal": Vector2.DOWN, "desc": "up"}
    else:
        return {"p1": Vector2(-w, h), "p2": Vector2(w, h), "normal": Vector2.UP, "desc": "down"}

## 返回矩形外的一个点,沿以Vector2.RIGHT为初始方向与矩形相交的第一条边的交点和法线向量
static func get_first_rect_collision_normal(rect: Rect2, point: Vector2, direction: Vector2) -> Dictionary:
    var edge_t: float = 0
    var edge_data: Dictionary = {}
    if direction.x != 0:
        # 左边
        var t: float = (rect.position.x - point.x) / direction.x
        if t >= 0:
            var intersect_y: float = point.y + t * direction.y
            if intersect_y >= rect.position.y and intersect_y <= (rect.position.y + rect.size.y):
                edge_t = t
                edge_data = {"normal": Vector2.RIGHT, "p": Vector2(rect.position.x, intersect_y)}
        # 右边
        t = (rect.end.x - point.x) / direction.x
        if t >= 0:
            var intersect_y = point.y + t * direction.y
            if intersect_y >= rect.position.y and intersect_y <= rect.end.y:
                if edge_t <= 0 or edge_t > t:
                    edge_t = t
                    edge_data = {"normal": Vector2.LEFT, "p": Vector2(rect.end.x, intersect_y)}
    if direction.y != 0:
        # 顶边
        var t = (rect.position.y - point.y) / direction.y
        if t >= 0:
            var intersect_x = point.x + t * direction.x
            if intersect_x >= rect.position.x and intersect_x <= rect.end.x:
                if edge_t <= 0 or edge_t > t:
                    edge_t = t
                    edge_data = {"normal": Vector2.DOWN, "p": Vector2(intersect_x, rect.position.y)}
        # 底边
        t = (rect.end.y - point.y) / direction.y
        if t >= 0:
            var intersect_x = point.x + t * direction.x
            if intersect_x >= rect.position.x and intersect_x <= rect.end.x:
                if edge_t <= 0 or edge_t > t:
                    edge_t = t
                    edge_data = {"normal": Vector2.UP, "p": Vector2(intersect_x, rect.end.y)}
    return edge_data

## 返回矩形以中心点Vector2.RIGHT为初始方向,顺时针旋转指定角度后于边框的交点
static func get_rect_center_intersect_point(center_size: Vector2, angle: float) -> Vector2:
    var cross_data: Dictionary = get_rect_center_intersect_line(center_size, angle)
    var cross_line: StraightLine = StraightLine.from_point(cross_data.p1, cross_data.p2)
    var center_up_line: StraightLine = StraightLine.from_direction(Vector2.RIGHT.rotated(angle))
    return cross_line.get_intersect_point(center_up_line)

# 在指定大小的矩形内随机获取一个坐标点
static func get_rand_rect_point(rect_size: Vector2) -> Vector2:
    var is_x: bool = randi() % 2 == 0
    var x: float = 0
    var y: float = 0
    if is_x:
        x = randf_range(0, rect_size.x)
        y = [0, rect_size.y][randi() % 2]
    else:
        y = randf_range(0, rect_size.y)
        x = [0, rect_size.x][randi() % 2]
    return Vector2(x, y)

# 获取一组按指定步长偏移的角度值
static func get_split_angles(max_split_count: int, step_angle: float) -> Array:
    var split_angles: Array = []
    var start_angle: float = 0
    if max_split_count % 2:
        split_angles.append(0)
        start_angle = step_angle
    else:
        start_angle = step_angle / 2
    for i in range(int(max_split_count / 2)):
        var v: float = start_angle + step_angle * i
        split_angles.append(v)
        split_angles.append(-v)
    return split_angles

# 获取圆
static func circle(radius: float = 1.0, center: Vector2 = Vector2.ZERO) -> PackedVector2Array:
    var segments: int = int(4 * floor(radius / 16.0) + 16)
    var points: Array = []
    points.resize(segments + 1)
    var segment_size: float = TAU / segments
    for i in range(segments):
        points[i] = Vector2(cos(i * segment_size) * radius + center.x, sin(i * segment_size) * radius + center.y)
    points[segments] = points[0]
    return PackedVector2Array(points)

static func rectangle(extents: Vector2) -> PackedVector2Array:
    return PackedVector2Array([
        Vector2(-extents.x, -extents.y), Vector2(extents.x, -extents.y),
        Vector2(extents.x, extents.y), Vector2(-extents.x, extents.y),
    ])

static func capsule(radius: float, height: float) -> PackedVector2Array:
    radius = clamp(radius, 0.5, INF)
    height = clamp(height, 0.0, INF)
    var hheight: float = clamp( height / 2.0, 0, INF )
    var cap_circle: PackedVector2Array = circle(radius)
    var points: Array = []
    points.resize(len(cap_circle) + 2)

    var arch_count: int = int((len(cap_circle) / 2.0) + 1)
    for i in range(arch_count):
        points[i] = cap_circle[i] + Vector2(0, hheight)

    for i in range(arch_count - 1, len(cap_circle)):
        points[i + 1] = cap_circle[i] + Vector2(0, -hheight)
    points[-1] = points[0]
    return PackedVector2Array(points)

# 获取箭头
static func arrow(length: float) -> PackedVector2Array:
    length = 1.0  if is_zero_approx(length) else clamp(abs(length), 1.0, INF) * sign(length)
    var tip: Vector2 = Vector2.DOWN * length
    var head_dw: float = clamp(length * 0.1, 1, 4)
    var head_dh: float = clamp(length * 0.2, 2, 8)
    return PackedVector2Array([
        # stem
        tip + Vector2(-0.51, -head_dh), Vector2(-0.51, 0.0), Vector2(0.51, 0.0), tip + Vector2(0.51, -head_dh),
        # head
        tip + Vector2(head_dw, -head_dh), tip, tip + Vector2(-head_dw, -head_dh),
    ])

static func concave_to_polygons(p_segments: PackedVector2Array) -> PackedVector2Array:
    if not len(p_segments):
        return PackedVector2Array()
    var result: PackedVector2Array = PackedVector2Array([p_segments[0]])
    for i in range(1, len(p_segments)):
        #segment tail
        if (i % 2):
            result.push_back(p_segments[i])
        #segment head
        else:
            if p_segments[i].distance_squared_to(p_segments[i-1]) > 0.01:
                # disjoint segment started
                result.push_back(p_segments[i])
    return result

static func segment(a: Vector2, b: Vector2) -> PackedVector2Array:
    var hthick: Vector2 = (a - b).orthogonal().normalized() * 0.51
    return PackedVector2Array([
        a + hthick, b + hthick, b - hthick, a - hthick
    ])

static func line_to_polygon(normal: Vector2, d: float) -> PackedVector2Array:
    normal = normal.normalized()
    var tangent: Vector2 = normal.orthogonal() * 2048.0
    return PackedVector2Array([
        tangent + (normal * d), -tangent + (normal * d),
        -tangent + (normal*(d - 1.01)), tangent + (normal * (d-1.01)),
    ])

static func get_shape2d_polygon(shape: Shape2D) -> PackedVector2Array:
    var polygon: PackedVector2Array = PackedVector2Array()
    if shape == null:
        pass
    elif shape is CapsuleShape2D:
        polygon = capsule(shape.radius, shape.height)
    elif shape is ConvexPolygonShape2D:
        polygon = shape.points
    elif shape is CircleShape2D:
        polygon = circle(shape.radius)
    elif shape is RectangleShape2D:
        polygon = rectangle(shape.extents)
    elif shape is ConcavePolygonShape2D:
        polygon = concave_to_polygons(shape.segments)
    elif shape is SegmentShape2D:
        polygon = segment(shape.a, shape.b)
    return polygon

# 绘制轨迹
static func draw_tail(target: Node2D, tail_line2D: Line2D, max_tail_count: int) -> void:
    var tail_count: int = tail_line2D.get_point_count()
    if tail_count > 0:
        var last_point: Vector2 = tail_line2D.points[tail_count - 1]
        if target.global_position == last_point:
            if tail_count > 1:
                tail_line2D.remove_point(0)
            return
    if tail_count > max_tail_count:
        tail_line2D.remove_point(0)
    tail_line2D.add_point(target.global_position)
